// KyPhoneUsbDrv.cpp : Defines the initialization routines for the DLL.
//

#include "stdafx.h"

#include "KyPhoneHidDrv.h"
#include "..\protocol\wjprotocol.h"

#define EXP_FUN KYPHONEHIDDRV_API

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#pragma comment(lib,"hid.lib")
extern "C" {
    #include "setupapi.h"
    #include "hidsdi.h"

};

#include "UsbIo.h"
#include "Qunue.h"

#include <dbt.h>

#include <stdlib.h>
#include <stdio.h>


void debug_hex(const char* info, void* buf, int len) {
    char txt[1024];
    strcpy(txt, info);
    char* p = txt + strlen(txt);
    BYTE* pd = (BYTE*)buf;
    while (len--) {
        sprintf(p, " %02X", *pd++);
        p += 3;
    }
    *p++ = '\n';
    *p = '\0';
    OutputDebugString(txt);
}



#define     DEFALT_DEV                      ((HANDLE)(-3))
#define     BROADCAST_HANDLE                ((HANDLE)(-2))


HANDLE hPipe = INVALID_HANDLE_VALUE;
int logHid(void * buff, int len) {
    if (hPipe == INVALID_HANDLE_VALUE) {
        //if (WaitNamedPipe("\\\\.\\Pipe\\mypipe", NMPWAIT_WAIT_FOREVER) == FALSE)
        //    return 0;

        hPipe = CreateFile("\\\\.\\Pipe\\mypipe", GENERIC_READ | GENERIC_WRITE, 0,
            NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);


    }
    if (hPipe == INVALID_HANDLE_VALUE) {
        UINT32 err = GetLastError();
        printf("%d", err);
        return 0;
    }
    DWORD dwWrite;
    WriteFile(hPipe, buff, len, &dwWrite, NULL);

}
/* Low Level Device Interface*/
/*****************************************************
*  Read function, 
*****************************************************/
static DWORD  WINAPI thread_read_8258(LPVOID lpThreadParameter )
{
    MC_DEVICES * pDev = (MC_DEVICES *)lpThreadParameter;
	if( pDev == NULL ) return 0;

	pDev->bTaskRun = 1;

    HANDLE hWaitObject[3] ;
    hWaitObject[0] = pDev->OvRd.hEvent;
    hWaitObject[1] = g_hUsbDevMap.hEVTUsbExit;
    hWaitObject[2] = pDev->hEvtDemount;

    while(1)
    {   
        BYTE buff[32];
        DWORD TransBytes = 0;

        if(!ReadFile(pDev->hRead, buff, 9, &TransBytes, &pDev->OvRd))
        {
            DWORD error = GetLastError();
            if(error == ERROR_IO_PENDING)
            {

                DWORD dwEventid = WaitForMultipleObjects(3, hWaitObject, FALSE, INFINITE );
                if( dwEventid == WAIT_OBJECT_0 )
                {
                    if(!GetOverlappedResult(pDev->hRead, &pDev->OvRd, &TransBytes, TRUE))
                    {
                        break;
                    }
                    error = GetLastError();
                    if(error == ERROR_DEVICE_NOT_CONNECTED)
                    {
                        break;
                    }               
                }
                if( dwEventid >= WAIT_OBJECT_0 + 1 )
                {
                    break;
                }
            }
        }        

        DWORD dwEventid = WaitForMultipleObjects(2, &hWaitObject[1], FALSE, 0);
        if( dwEventid == WAIT_OBJECT_0 || dwEventid == (WAIT_OBJECT_0+1) )
        {
            break;
        }
        if( TransBytes )
        {
            INT len = buff[1];
            debug_hex("HID-R: ", buff, 7);
            if (len < TransBytes - 1) {
                //SendMessage(::AfxGetMainWnd(), WM_USER + 1279, (WPARAM)(&buff[2]), 0);
                PostToQunue(pDev->hInBuff, &buff[2], len);
                //logHid(&buff[2], len);
                g_hUsbDevMap.pCallbackDec(GetHandle(pDev));
            }
            continue;
        }
        else
        {
            SuspecDevicePlugOut();
        }

    }
    CancelIo(pDev->hRead);
	pDev->bTaskRun = 0;
    MyTrace(">>>>>> Usb Read Thead (m99) End!");
    return 0;
}

static BOOL read_device(HANDLE hCom, USHORT InputReportLen, BYTE* InputReport)
{
    memset( InputReport, 0, InputReportLen);
    bool found =false;
    DWORD TransBytes;
    
    //-------------------------------
    return ReadFile( hCom, InputReport, InputReportLen, &TransBytes, NULL);
}

/*****************************************************
*  Write function,  IOCTL_USB_GET_NODE_CONNECTION_INFORMATION 
*****************************************************/
static BOOL write_device(HANDLE hDevice, USHORT OutputReportLen,BYTE *Output)
{
   
    
    MC_DEVICES * pDev = (MC_DEVICES*)hDevice;   
    BYTE OutputReport[255] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; //new BYTE[OutputReportLen+15];
    bool found =false;
    DWORD TransBytes;
    OutputReport[0] = 4;
    OutputReport[1] = (BYTE)OutputReportLen;
    memcpy(&OutputReport[2], (BYTE *)Output, OutputReportLen);

#if 0
    volatile static int rptlen = 32;
    BYTE odt[0x21] = { 4 , 0, 3,9 ,7};
    BOOL bret = HidD_SetOutputReport(pDev->hWrite, odt, rptlen++);
    if( !bret ){
        int errv = GetLastError();
        char str[64]; 
        sprintf(str, "SetOutputReport %dB err:%d\n", rptlen-1, errv);
        OutputDebugString(str);
    }
#endif
    if( !WriteFile( pDev->hWrite, OutputReport,9, &TransBytes, &pDev->OvWr))
    {
        int err = GetLastError();
        printf("ERROR: %d\n", err);
        if(GetLastError() == ERROR_IO_PENDING)
        {
            HANDLE hWaitObject[2] ;
            hWaitObject[0] = pDev->OvWr.hEvent;
            hWaitObject[1] = g_hUsbDevMap.hEVTUsbExit;
            DWORD dwEventid = WaitForMultipleObjects(2, hWaitObject, FALSE, INFINITE );
            if( dwEventid == WAIT_OBJECT_0 )
            {
                if(!GetOverlappedResult(pDev->hWrite, &pDev->OvWr, &TransBytes, TRUE))
                {
                    return false;
                }
                if(GetLastError() == ERROR_DEVICE_NOT_CONNECTED)
                {
                    return false;
                }               
            }
            if( dwEventid == WAIT_OBJECT_0 + 1 )
            {
                return false;
            }
            
        }
    }
    return TRUE;
}
/////////////////////////////////////////////////////////////////////////

INT ReadUSBDev_8258(HANDLE hDevice, void * pbuf, int dwSize,\
               int timeout,DWORD * pErr)
{
    //return (INT)read_device(HANDLE hCom, USHORT InputReportLen)
    MC_DEVICES * pDev = GetUsbDviceHandle(hDevice); 

    BYTE * ptr = (BYTE *)pbuf;


    if( pDev == NULL ){ Sleep(timeout);  return 0;}
    int len; 
    do{
        len = GetQunueUsed(pDev->hInBuff);
        if( len )
        {
            if( dwSize > len ) dwSize = len;
            ::PeekFromQunue( pDev->hInBuff,ptr,dwSize);
            break;
        }
        Sleep(10);
        timeout -= 10;
    }while( timeout < 0);


    return len;
}



typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef signed char s8;
typedef signed short s16;
typedef signed int s32;
#define PACKET_DEALY_MS 50

enum {
    PHONE_IDLE,
    PHONE_GOT_LEADER,
    PHONE_GOT_LEN,
    PHONE_GOT_CMD,
    PHONE_BLOCK,
    PHONE_ERR,
};



typedef struct te_prot {
    u8 state;
    u8 len;
    u8 rxb;
    u8 cmd;
    u32 tick_block;

}PHONEDRV_T;

PHONEDRV_T mphone = {  PHONE_IDLE, 0 };
void wj_printf(const char* f, int l, const char* pszFormat, ...)
{
    char buf[1024];
    sprintf_s(buf, 1024, "[%lu-%s.%d]: ", GetTickCount(), f, l);
    va_list arglist;
    va_start(arglist, pszFormat);
    vsprintf_s(&buf[strlen(buf)], 1024 - strlen(buf), pszFormat, arglist);
    va_end(arglist);
    strcat_s(buf, 1024, "\n");
    OutputDebugStringA(buf);
}

int parse_packet(PHONEDRV_T *phone, const u8* packet, int len) {
    int i;
    u32 ticknow = GetTickCount();
    if (ticknow > phone->tick_block ) {
        phone->state = PHONE_IDLE;
    }
    if (phone->state == PHONE_BLOCK) {
        if (ticknow < mphone.tick_block) {
            int delay = mphone.tick_block - ticknow;
            wj_printf("tl8258_w", __LINE__, " delay %d ms", delay);
            DelayUs(delay * 1000);
        }
        phone->state = PHONE_IDLE;
    }
    for (i = 0; i < len; i++) {
        u8 val = packet[i];

        switch (phone->state) {
        case PHONE_IDLE:
            if (val == 0xFF)  phone->state = PHONE_GOT_LEADER;
            break;
        case PHONE_GOT_LEADER:
            if (val != 0xFF) {
                phone->len = val;
                phone->state = PHONE_GOT_LEN;
                phone->rxb = 0;
            }
            break;
        case PHONE_GOT_LEN:
            phone->cmd = val;
            phone->state = PHONE_GOT_CMD;

            if (--phone->len == 0) {
                phone->state = PHONE_BLOCK;
                phone->tick_block = ticknow + PACKET_DEALY_MS;
                return i+1;
            }
            break;
        case PHONE_GOT_CMD:
            if (--phone->len == 0) {
                phone->state = PHONE_BLOCK;
                phone->tick_block = ticknow + PACKET_DEALY_MS;
                return i+1;
            }
            break;
        case PHONE_BLOCK:
            return i;
        case PHONE_ERR:
            phone->state = PHONE_IDLE;
            break;
        }
    }
    return i;
}
//
//BYTE ldbg[] = {
//0xFF,0x09,0x25,0xA1,0x12,0x34,0x56,0x78,0x90,0x12,0x34,0xFF,
//0xFF,0x09,0x25,0xA2,0x12,0x34,0x56,0x78,0x90,0x12,0x34,0xFF,
//0xFF,0x09,0x25,0xA3,0x12,0x34,0x56,0x78,0x90,0x12,0x34,0xFF,
//0xFF,0x09,0x25,0xA4,0x12,0x34,0x56,0x78,0x90,0x12,0x34,0xFF,
//0xFF,0x09,0x25,0xA5,0x12,0x34,0x56,0x78,0x90,0x12,0x34,0xFF,
//0xFF,0x09,0x25,0xA6,0x12,0x34,0x56,0x78,0x90,0x12,0x34,0xFF,
//0xFF,0x09,0x25,0xA7,0x12,0x34,0x56,0x78,0x90,0x12,0x34,0xFF,
//0xFF,0x09,0x25,0xA8,0x12,0x34,0x56,0x78,0x90,0x12,0x34,0xFF,
//0xFF,0x09,0x25,0xA9,0x12,0x34,0x56,0x78,0x90,0x12,0x34,0xFF,
//0xFF,0x09,0x25,0xAa,0x12,0x34,0x56,0x78,0x90,0x12,0x34,0xFF
//};

static INT write_device(HANDLE hDevice, void* pvbuf, int dwSize, DWORD* err) {
    BYTE* pbuf = (BYTE*)pvbuf;
    int nWriteLen = dwSize;
    MC_DEVICES* pDev = GetUsbDviceHandle(hDevice);
    static u32 tick_block = 0;
    //BYTE dbgcmd[] = { 0xff, 0x01, 0x65 };
    //if (dwSize == 3 && memcmp(pvbuf, dbgcmd, sizeof(dbgcmd)) == 0) {
    //    return write_device(hDevice, ldbg, sizeof(ldbg), err);
    //}
    static u32 tick_last_pack = 0;
    u32 tnow = GetTickCount();
    if (tnow < tick_last_pack) {
        DelayUs((tick_last_pack - tnow) * 1000);
    }
    
    while (pDev != NULL)
    {
        BYTE* psrc = (BYTE*)pbuf;
        if (pvbuf)
        {
            char sdbug[256*3] = "tl8258:";
            char* ptr = &sdbug[strlen(sdbug)];
            for (int i = 0; i < dwSize; i++)
            {
                sprintf(ptr, " %02X", *((BYTE*)pvbuf + i));
                ptr += strlen(ptr);
            }
            wj_printf( __func__, __LINE__, "%s\n", sdbug);
        }

        while (dwSize)
        {
            u32 ticknow = GetTickCount();
            if( ticknow < tick_block ) {
                DelayUs( (tick_block-ticknow)*1000);
            }
            tick_block = GetTickCount() + 15;
            int len = dwSize > 6 ? 6 : dwSize;
            if (FALSE == write_device(pDev, len, (BYTE*)pbuf))
                break;
            pbuf += len;
            dwSize -= len;
        }
        if (hDevice != BROADCAST_HANDLE) {
            tick_last_pack = GetTickCount() + 100;
            return dwSize++;
        }

        pDev = GetUsbDviceHandle(pDev);
        pbuf = (BYTE*)pvbuf;
        dwSize = nWriteLen;
    }

    tick_last_pack = GetTickCount() + 100;
    return 0;
}

static INT WriteUSBDev_8258(HANDLE hDevice , void* pvbuf, int dwSize, DWORD * err)
{
    //Phrase the package and send diffrent package at diffrent output time
    BYTE* pbuf = (BYTE* )pvbuf;
    int nWriteLen = dwSize;
    int ret = 0;
    DWORD tsnow = GetTickCount();

    do {
        int splidlen = parse_packet(&mphone, pbuf, nWriteLen);
        if (splidlen != 0) {
            ret = write_device(hDevice, pbuf, splidlen, err);
            nWriteLen -= splidlen;
        }
        else {
            break;
        }
    } while (nWriteLen > 0);
    
    return ret;
}



const HID_DRIVER drv_telink8258 = {

    CHIP_TELINK8258,
    0xff01,//0xFFA0, //0x000c,
    0,
    0x4155, //0x8001,//G_PID_TL8258,
    0x4C4A, //0x2488,//G_VID_TELINK,
    thread_read_8258,
    ReadUSBDev_8258,
    WriteUSBDev_8258,
    0x800a,
};

/*
8 idVendor 2 4C4Ah
10 idProduct 2 4155h
12 bcdDevice 2 0100h 1.00

*/